package com.hourse.platform.filter;

import com.hourse.platform.aspect.Platform;
import com.hourse.platform.aspect.PlatformType;
import com.hourse.platform.utils.AnnotationUtil;
import com.hourse.platform.vo.UserSession;
import com.hourse.platform.common.ErrorMsg;
import com.hourse.platform.common.LoginKey;
import com.hourse.platform.utils.CurrentThread;
import com.hourse.platform.utils.StringTools;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
@WebFilter(urlPatterns = "/*")
@Slf4j
public class SecurityFilter implements Filter {
    // 用于存储平台对应请求的初始化信息
    private static Map<String, List<String>> platformList = new HashMap<>();

    private static List<String> IGNORE_PATH = new ArrayList<>();

    @Autowired
    WebApplicationContext applicationContext;

    @Autowired
    AnnotationUtil annotationUtil;

    /**
     * SESSION 有效时间 （秒）
     */
    private static Integer LoginKeyTime;

    @Autowired
    private RedisTemplate redisTemplate;

    // 用于创建MultipartHttpServletRequest
    private MultipartResolver multipartResolver = null;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        multipartResolver = ((MultipartResolver) applicationContext.getBean("multipartResolver", MultipartResolver.class));
        log.info("初始化地址过滤器，，，");
        // 通过注解获取要过滤的路径
        RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        // 获取url与类和方法的对应信息
        Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
        // 用于存储所有被RequestMapping 注释的方法
        List<String> urlList = new ArrayList<String>();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> m : map.entrySet()) {
            RequestMappingInfo info = m.getKey();
            PatternsRequestCondition p = info.getPatternsCondition();
            for (String url : p.getPatterns()) {
                urlList.add(url);
            }
        }

        // 获取对应平台接口
        try {
            Map<String, Map<String, Object>> allAddTagAnnotationUrl =
                    annotationUtil.getAllAddTagAnnotationUrl("classpath:com/hourse/platform/controller/*.class", Platform.class);
            allAddTagAnnotationUrl.putAll(annotationUtil.getAllAddTagAnnotationUrl("classpath:com/hourse/app/controller/*.class", Platform.class));

            allAddTagAnnotationUrl.forEach((s, stringObjectMap) -> {
                // s: 路径 ，stringObjectMap: 注解携带参数
                if (urlList.contains(s)) {
                    PlatformType[] type = (PlatformType[]) stringObjectMap.get("type");
                    List l = null;
                    log.info("平台：{}，地址：{}", type, s);
                    for (PlatformType p : type) {
                        if (platformList.containsKey(p.toString())) {
                            l = platformList.get(p.toString());
                        } else {
                            l = new ArrayList();
                        }
                        l.add(s);
                        // 对应平台存储可以调用的路径
                        platformList.put(p.toString(), l);
                    }
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
        log.info("获取成功");

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        String queryString = request.getQueryString();
        queryString = StringTools.isNull(queryString) ? "" : "?" + queryString;

        log.info("【当前访问】{}", request.getRequestURL() + queryString);
        log.info("【客户端IP】{}", GetIpAddress(request));
        CurrentThread.put("clientIp", GetIpAddress(request));

        // 登陆系统类型 为多平台登陆预留
        String stype = request.getHeader("stype");
        String token = request.getHeader("token");
        CurrentThread.put("stype", stype);
        CurrentThread.put("token", token);

        log.info("【访问系统】{}", stype);
        log.info("【用户凭证】{}", token);

        Boolean isPass = isAllow(IGNORE_PATH, request);

        if (isPass) {
            resetSession(stype, token);
            filterChain.doFilter(servletRequest, servletResponse);
        }else {
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=UTF-8");
            UserSession userSession = resetSession(stype, token);
            List<String> strings = platformList.get(stype);

            if (userSession != null) {
                if (userSession.isAdmin() || isAllow(userSession.getAllowPaths(), request)) {
                    filterChain.doFilter(servletRequest, servletResponse);
                } else if (StringTools.isNotNull(stype) && PlatformType.ANDROID.toString().equals(stype) && getContains(strings, request.getServletPath())) {
                    // 安卓端直接放过,不进行接口权限校验
                    filterChain.doFilter(servletRequest, servletResponse);
                } else {
                    response.getWriter().print(ErrorMsg.AUTH_ERR.Build().toJson());
                    log.info("【权限不足】{}", request.getRequestURI());
                }
            } else {
                response.getWriter().print(ErrorMsg.TOKEN_TIME_OUT.Build().toJson());
                log.info("【用户登录失效】{}", request.getRequestURI());
            }

        }
    }

    private boolean getContains(List<String> list, String path) {
        // 获取请求
        if (list.contains(path)) {
            return true;
        } else {
            for (String s : list) {
                int i = s.indexOf("{");
                if (i > 0 && (i + 1) <= path.length()) {
                    if (s.substring(0, i).equals(path.substring(0, i))) {
                        return true;
                    }
                }
            }
            return false;
        }
    }

    private UserSession resetSession(String stype, String token) {

        String loginKey = new LoginKey(stype, token).getKey();
        UserSession userSession = (UserSession) redisTemplate.opsForValue().get(loginKey);
        if (userSession != null && StringTools.isNotNull(userSession.getUserId())) {
            log.info("【用户ID】{}", userSession.getUserId());
            // 重设有效时间 2 分钟
            redisTemplate.expire(loginKey, LoginKeyTime, TimeUnit.SECONDS);
            redisTemplate.expire(userSession.getAccount(), LoginKeyTime, TimeUnit.SECONDS);

            CurrentThread.setUserId(userSession.getUserId());
            CurrentThread.setAdminStatus(userSession.isAdmin());
            CurrentThread.setRoles(userSession.getRoles());
            CurrentThread.setCurrentRoleId(userSession.getCurtRoleGroupId());
            CurrentThread.setCurrentEnterPriseId(userSession.getCurrentEnterPriseId());
            return userSession;
        } else {
            CurrentThread.setUserId(null);
            CurrentThread.setAdminStatus(false);
            CurrentThread.setRoles(null);
        }

        log.warn("警告: 当前用户 SESSION 已失效...");

        return null;
    }

    boolean isAllow(List<String> path, HttpServletRequest request) {


        for (String pattern : path) {
            if (StringTools.urlAanalyze(pattern, request.getRequestURI().replaceFirst(request.getContextPath(), ""))) {
                return true;
            }
        }
        return false;
    }


    @Value("${security.ignorePath}")
    public void setIgnorePath(String ignorePath) {
        String[] allowPath = ignorePath.split("\\,");

        for (String path : allowPath) {
            IGNORE_PATH.add(path);
        }
    }

    @Value("${loginKeyTime}")
    public void setLoginKeyTime(Integer loginKeyTime) {
        LoginKeyTime = loginKeyTime;
    }

    public static String GetIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}
